/**
 * 
 * @author Andrew Bell
 * 
 */
import java.util.Random;

public class RollDAOImpl implements RollDAO {
	private static int MAX_ROLL = 46340;
    
	/**
     * Returns an {@link Integer} value representing the dice roll when passed a {@link String}.
     * <p>
     * The <code>inputDiceString</code> should be in a format of rrDss where rr is the amount of dice to roll
     * and ss in the amount of sides to the dice (for example: 12D10 or 3D6).
     * @param inputDiceString   input string representing the dice roll
     * @return                  result of the dice roll
     * @throws DiceException    if DiceHandler encounters an error attempting to roll the dice
     * @see Roll
     * @see Dice
     */
	public int rollFromString(String inputDiceString) throws DiceException {
        // Create Roll and Dice
        Dice newDice = new Dice(findTypeDice(inputDiceString));
        Roll newRoll = new Roll(findRolls(inputDiceString),newDice);
        // Validate for valid reasonable input
        if ( (newDice.getSides() > MAX_ROLL) || (newRoll.getAmntOfDice() > MAX_ROLL) ){
        	throw new DiceException("Dice values are too high to compute.");
        } else {
        	// roll dice
        	for(int r =0;r<findRolls(inputDiceString);r++){
              newRoll.setRollTotal(newRoll.getRollTotal()+rollDice(newDice));
          }
        }
        // return total
        return newRoll.getRollTotal();
    }

	/**
     * Simulates a dice roll by fetching a random number between 1 and the size of the die.
     * @param newDice	Dice object that contains amount of sides
     * @return          result of the rolled die
     */
    private static int rollDice(Dice newDice) {
        Random randomGenerator = new Random();
        return (randomGenerator.nextInt(newDice.getSides())+1);
    }

	/**
     * Returns the sides on the dice to be rolled.
     * @param inputDiceString   input string representing the dice roll
     * @return                  amount of sides on dice to be rolled
     * @throws DiceException    if an invalid string is passed, DiceException is thrown
     */
    private static int findTypeDice(String inputDiceString) throws DiceException {
        try{
        	int typeDice = Integer.parseInt(inputDiceString.substring(findD(inputDiceString)+1));
        	if (typeDice < 1){
        		throw new DiceException("Cannot roll less than 1 sided dice.");
        	}
            return typeDice;
        } catch (Exception e) {
            throw new DiceException("Error finding dice type.");
        }
    }

	/**
     * Returns the location of where the character 'D' or 'd' is located in the string.
     * If there are multiple 'D' characters, or none, an en exception is thrown
     * @param inputDiceString   input string representing the dice roll
     * @return                  location of the 'D' within the string
     * @throws DiceException    Either multiple, or no 'D' characters found
     */
    private static int findD(String inputDiceString) throws DiceException{
        int dSpot = -1;
        for(int i = 0;i<inputDiceString.length();i++){
            if (inputDiceString.substring(i,i+1).equalsIgnoreCase("D")) {
                if (dSpot != -1) {
                    throw new DiceException("More than one D in argument");
                } else {
                    dSpot = i;
                }
            }
        }
        if (dSpot == -1) {
            throw new DiceException("No D found in argument");
        }
        return dSpot;
    }

	/**
     * Returns the amount of dice to be rolled, if none found, returns 1
     * @param inputDiceString   input string representing the dice roll
     * @return                  amount of dice to be rolled
     * @throws DiceException    if an invalid string is passed, DiceException is thrown
     */
    private static int findRolls(String inputDiceString) throws DiceException {
        int numberOfDice;
        int dLocation = findD(inputDiceString);

        if(dLocation==0){
            numberOfDice = 1;
        }else{
            try{
                numberOfDice = Integer.parseInt(inputDiceString.substring(0,dLocation));
            } catch (Exception e) {
                throw new DiceException("Error finding roll quantity.");
            }
        }
            return numberOfDice;
    }

}
